using System;
using System.Collections.Generic;
using System.Text;
using NetFwTypeLib;

namespace NAst.Common
{
    [Netlet("OpenFirewallPort")]
    public class WindowsFirewallChangerNetlet : INetlet
    {
        bool _hasBeenOpened = false;
        int _port = 0;
        NET_FW_IP_PROTOCOL_ _protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP;

        #region INetlet Members

        public void Initialize(string configurationName, System.Xml.XmlElement config)
        {
            string port = config.GetAttribute("port");
            if (!int.TryParse(port, out _port))
                throw new ArgumentException("invalid port : " + port);
            switch (config.GetAttribute("protocol").ToLower())
            {
                case "":
                    throw new ArgumentException("please specify tcp or udp protocol");
                case "udp":
                    _protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_UDP;
                    break;
                case "tcp":
                    _protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP;
                    break;
                default:
                    throw new ArgumentException("invalid protocol : " + config.GetAttribute("protocol"));
            }
        }

        public void Connect()
        {
            // this netlet does nothing on windows vista
            // because opening a new port would need privilege
            // elevation. As bothering the user with a UAC
            // popup is not what I wanted, nothing is done if
            // the windows version is >= 6.

            if (Environment.OSVersion.Version.Major >= 6)
                return;

            Type t = Type.GetTypeFromProgID("HNetCfg.FwMgr");
            INetFwMgr firewall = Activator.CreateInstance(t) as INetFwMgr;
            
            object allowed, restricted;
            firewall.IsPortAllowed(null,NET_FW_IP_VERSION_.NET_FW_IP_VERSION_V4, _port,null, _protocol, out allowed, out restricted);
            if (allowed is bool && ((bool)allowed))
                return;
            INetFwProfile profile = firewall.LocalPolicy.CurrentProfile;
            
            t = Type.GetTypeFromProgID("HNetCfg.FWOpenPort");
            INetFwOpenPort port = Activator.CreateInstance(t) as INetFwOpenPort;
            port.Port = _port;
            port.Protocol = _protocol;
            port.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
            port.Enabled = true;
            port.Name = "Network openend port " + _port;
            profile.GloballyOpenPorts.Add(port);
            _hasBeenOpened = true;
        }

        public void Disconnect()
        {
            if (!_hasBeenOpened)
                return;
            
            Type t = Type.GetTypeFromProgID("HNetCfg.FwMgr");
            INetFwMgr firewall = Activator.CreateInstance(t) as INetFwMgr;
            firewall.LocalPolicy.CurrentProfile.GloballyOpenPorts.Remove(_port, _protocol);
            _hasBeenOpened = false;
        }

        public void SaveState(System.Xml.XmlElement elmToSaveTo)
        {
            elmToSaveTo.SetAttribute("hasBeenOpened", _hasBeenOpened.ToString());
            elmToSaveTo.SetAttribute("protocol", _protocol==NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP?"tcp":"udp");
            elmToSaveTo.SetAttribute("port", _port.ToString());
        }

        public void LoadState(System.Xml.XmlElement elmToLoadFrom)
        {

            _hasBeenOpened = false;
            bool.TryParse(elmToLoadFrom.GetAttribute("wasFolderCreated"), out _hasBeenOpened);
            
            _port = 0;
            int.TryParse(elmToLoadFrom.GetAttribute("wasFolderCreated"), out _port);

            switch (elmToLoadFrom.GetAttribute("protocol").ToLower())
            {
                case "": 
                    break;
                case "udp":
                    _protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_UDP;
                    break;
                case "tcp":
                    _protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP;
                    break;
            }
        }

        #endregion
    }
}
